use std::error::Error;
use std::sync::Arc;
use tonic::Status;

#[allow(dead_code)]
pub trait StatusExt {
    fn ok_with_source(
        message: impl Into<String>,
        source: impl Error + Send + Sync + 'static,
    ) -> Status;
    fn cancelled_with_source(
        message: impl Into<String>,
        source: impl Error + Send + Sync + 'static,
    ) -> Status;
    fn unknown_with_source(
        message: impl Into<String>,
        source: impl Error + Send + Sync + 'static,
    ) -> Status;
    fn invalid_argument_with_source(
        message: impl Into<String>,
        source: impl Error + Send + Sync + 'static,
    ) -> Status;
    fn deadline_exceeded_with_source(
        message: impl Into<String>,
        source: impl Error + Send + Sync + 'static,
    ) -> Status;
    fn not_found_with_source(
        message: impl Into<String>,
        source: impl Error + Send + Sync + 'static,
    ) -> Status;
    fn already_exists_with_source(
        message: impl Into<String>,
        source: impl Error + Send + Sync + 'static,
    ) -> Status;
    fn resource_exhausted_with_source(
        message: impl Into<String>,
        source: impl Error + Send + Sync + 'static,
    ) -> Status;
    fn failed_precondition_with_source(
        message: impl Into<String>,
        source: impl Error + Send + Sync + 'static,
    ) -> Status;
    fn aborted_with_source(
        message: impl Into<String>,
        source: impl Error + Send + Sync + 'static,
    ) -> Status;
    fn out_of_range_with_source(
        message: impl Into<String>,
        source: impl Error + Send + Sync + 'static,
    ) -> Status;
    fn unimplemented_with_source(
        message: impl Into<String>,
        source: impl Error + Send + Sync + 'static,
    ) -> Status;
    fn internal_with_source(
        message: impl Into<String>,
        source: impl Error + Send + Sync + 'static,
    ) -> Status;
    fn unavailable_with_source(
        message: impl Into<String>,
        source: impl Error + Send + Sync + 'static,
    ) -> Status;
    fn data_loss_with_source(
        message: impl Into<String>,
        source: impl Error + Send + Sync + 'static,
    ) -> Status;
    fn unauthenticated_with_source(
        message: impl Into<String>,
        source: impl Error + Send + Sync + 'static,
    ) -> Status;
}

fn set_source(mut status: Status, source: impl Error + Send + Sync + 'static) -> Status {
    status.set_source(Arc::new(source));
    status
}

impl StatusExt for Status {
    fn ok_with_source(
        message: impl Into<String>,
        source: impl Error + Send + Sync + 'static,
    ) -> Status {
        set_source(Self::ok(message), source)
    }

    fn cancelled_with_source(
        message: impl Into<String>,
        source: impl Error + Send + Sync + 'static,
    ) -> Status {
        set_source(Self::cancelled(message), source)
    }

    fn unknown_with_source(
        message: impl Into<String>,
        source: impl Error + Send + Sync + 'static,
    ) -> Status {
        set_source(Self::unknown(message), source)
    }

    fn invalid_argument_with_source(
        message: impl Into<String>,
        source: impl Error + Send + Sync + 'static,
    ) -> Status {
        set_source(Self::invalid_argument(message), source)
    }

    fn deadline_exceeded_with_source(
        message: impl Into<String>,
        source: impl Error + Send + Sync + 'static,
    ) -> Status {
        set_source(Self::deadline_exceeded(message), source)
    }

    fn not_found_with_source(
        message: impl Into<String>,
        source: impl Error + Send + Sync + 'static,
    ) -> Status {
        set_source(Self::not_found(message), source)
    }

    fn already_exists_with_source(
        message: impl Into<String>,
        source: impl Error + Send + Sync + 'static,
    ) -> Status {
        set_source(Self::already_exists(message), source)
    }

    fn resource_exhausted_with_source(
        message: impl Into<String>,
        source: impl Error + Send + Sync + 'static,
    ) -> Status {
        set_source(Self::resource_exhausted(message), source)
    }

    fn failed_precondition_with_source(
        message: impl Into<String>,
        source: impl Error + Send + Sync + 'static,
    ) -> Status {
        set_source(Self::failed_precondition(message), source)
    }

    fn aborted_with_source(
        message: impl Into<String>,
        source: impl Error + Send + Sync + 'static,
    ) -> Status {
        set_source(Self::aborted(message), source)
    }

    fn out_of_range_with_source(
        message: impl Into<String>,
        source: impl Error + Send + Sync + 'static,
    ) -> Status {
        set_source(Self::out_of_range(message), source)
    }

    fn unimplemented_with_source(
        message: impl Into<String>,
        source: impl Error + Send + Sync + 'static,
    ) -> Status {
        set_source(Self::unimplemented(message), source)
    }

    fn internal_with_source(
        message: impl Into<String>,
        source: impl Error + Send + Sync + 'static,
    ) -> Status {
        set_source(Self::internal(message), source)
    }

    fn unavailable_with_source(
        message: impl Into<String>,
        source: impl Error + Send + Sync + 'static,
    ) -> Status {
        set_source(Self::unavailable(message), source)
    }

    fn data_loss_with_source(
        message: impl Into<String>,
        source: impl Error + Send + Sync + 'static,
    ) -> Status {
        set_source(Self::data_loss(message), source)
    }

    fn unauthenticated_with_source(
        message: impl Into<String>,
        source: impl Error + Send + Sync + 'static,
    ) -> Status {
        set_source(Self::unauthenticated(message), source)
    }
}

